---
title: 'Multi-Language Processing'
description: 'Multi-Language Data Processing: Building a Unified Pipeline with Motia'
---

Modern backend development often requires combining the strengths of different programming languages. TypeScript for APIs, Python for data processing and AI, JavaScript for rapid prototyping. Traditional approaches involve complex microservices architectures with intricate communication patterns.

This comprehensive guide explores how to build a unified multi-language data processing pipeline using Motia's **step** primitive. We'll cover:

1. **Steps as Core Primitive**: How steps unify different languages under a single abstraction.
2. **Building the Pipeline**: A step-by-step guide to creating a cohesive multi-language data processing workflow.
3. **Unified Execution Model**: How steps enable seamless communication between different runtime environments.
4. **Hands-On Development**: How to build, run, and observe your unified multi-language pipeline.

Let's build a production-ready data processing system where steps unify TypeScript, Python, and JavaScript into a single cohesive workflow.

---

## Explore the Workbench

<div className="my-8">![Multi-Language Data Processing in Motia Workbench](/docs-images/motia-build-your-app-2.gif)</div>

**Try it yourself:**
- [View Source Code](https://github.com/MotiaDev/motia-examples/tree/main/examples/multi-language-data-processing)
- [Browse All Examples](https://github.com/MotiaDev/motia-examples)

---

## The Power of Steps: A Unified Multi-Language Primitive

At its core, our data processing pipeline demonstrates how **steps** solve the fundamental challenge of multi-language systems: unifying different programming languages under a single, coherent abstraction. Traditional polyglot architectures require complex inter-process communication and deployment coordination. Motia's **step** primitive unifies everything.

**Steps enable true language unification:**

- **[TypeScript](https://www.typescriptlang.org/)** steps: Strong typing and excellent tooling for APIs and orchestration
- **[Python](https://www.python.org/)** steps: Rich ecosystem for data processing, ML, and scientific computing  
- **[JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript)** steps: Dynamic processing and rapid development
- **[Motia's Step Primitive](https://motia.dev)**: The unifying abstraction that makes all languages work as a single system

Instead of managing multiple services, **steps** provide a single programming model. Whether written in TypeScript, Python, or JavaScript, every step follows the same pattern: receive data, process it, emit events. This unification is what makes multi-language development straightforward.

---

## The Anatomy of Our Multi-Language Pipeline

Our application consists of six specialized steps, each leveraging the optimal language for its specific task. Let's explore the complete architecture.

<Folder name="steps" defaultOpen>
  <File name="01-starter.step.ts" />
  <File name="02-bridge.step.ts" />
  <File name="simple-python_step.py" />
  <File name="notify.step.ts" />
  <File name="04-final.step.ts" />
  <File name="05-summary.step.js" />
</Folder>

<Folder name="types" defaultOpen>
  <File name="index.ts" />
</Folder>

<Tabs items={['api-starter', 'bridge-step', 'python-processor', 'notification-handler', 'finalizer', 'summary-generator']}>
  <Tab value="api-starter">
    The entry point for our multi-language workflow. This TypeScript API endpoint receives data, validates it with Zod schemas, and kicks off the processing pipeline.

```typescript
import { z } from 'zod'

const bodySchema = z.object({
  data: z.record(z.unknown()).optional(),
  message: z.string().optional()
})

// API endpoint to start the multi-language pipeline
export const config = {
  type: 'api',
  name: 'AppStarter',
  description: 'Start the multi-language app pipeline',

  method: 'POST',
  path: '/start-app',

  bodySchema,
  responseSchema: {
    200: z.object({
      message: z.string(),
      appId: z.number(),
      traceId: z.string()
    })
  },

  emits: ['app.started'],
  flows: ['data-processing']
} as const

export const handler = async (req: any, { logger, emit, traceId }: any) => {
  logger.info('🚀 Starting multi-language app', { body: req.body, traceId })
  
  const appData = {
    id: Date.now(),
    input: req.body.data || {},
    started_at: new Date().toISOString(),
    traceId
  }

  // Emit to next step
  await emit({
    topic: 'app.started',
    data: appData
  })

  logger.info('✅ App started successfully', { 
    appId: appData.id,
    traceId 
  })

  return {
    status: 200,
    body: {
      message: 'Multi-language app started successfully',
      appId: appData.id,
      traceId
    }
  }
}
```

  </Tab>
  <Tab value="bridge-step">
    A TypeScript bridge that receives the app start event, processes the data, and forwards it to the Python processing step with proper type transformation.

```typescript
import { z } from 'zod'

// Bridge step to connect app starter to Python processing
export const config = {
  type: 'event',
  name: 'AppBridge',
  description: 'Bridge between app start and Python processing',
  subscribes: ['app.started'],
  emits: ['data.processed'],
  input: z.object({
    id: z.number(),
    input: z.record(z.unknown()),
    started_at: z.string(),
    traceId: z.string()
  }),
  flows: ['data-processing']
} as const

export const handler = async (input: any, { logger, emit }: any) => {
  logger.info('🌉 Processing app data and sending to Python', { appId: input.id })
  
  // Process data for Python step
  const processedResult = {
    original_id: input.id,
    processed_at: input.started_at,
    result: `Processed: ${JSON.stringify(input.input)}`,
    confidence: 0.95,
    model_version: '1.0'
  }

  // Send to Python processing
  await emit({
    topic: 'data.processed', 
    data: processedResult
  })

  logger.info('✅ Data sent to Python processing', { 
    originalId: input.id
  })
}
```

  </Tab>
  <Tab value="python-processor">
    The core data processor written in Python, demonstrating how Python steps integrate seamlessly with the TypeScript workflow while maintaining access to Python's rich ecosystem. Note the `_step.py` naming convention.

```python
import time
from datetime import datetime

# Python processing step configuration
config = {
    "type": "event",
    "name": "ProcessDataPython",
    "description": "Process data using Python capabilities",
    "subscribes": ["data.processed"],
    "emits": ["python.done"],
    "flows": ["data-processing"]
}

async def handler(input_data, ctx):
    """
    Python step that processes data and demonstrates Python capabilities
    """
    logger = ctx.logger
    emit = ctx.emit
    
    # Extract data from input
    original_id = input_data.get("original_id")
    result = input_data.get("result", "")
    
    logger.info(f"🐍 Python processing data for ID: {original_id}")
    
    start_time = time.time()
    
    # Simulate Python data processing
    processed_message = f"Python processed: {result}"
    
    # Add some Python-specific processing
    data_analysis = {
        "word_count": len(result.split()) if isinstance(result, str) else 0,
        "character_count": len(result) if isinstance(result, str) else 0,
        "processed_timestamp": datetime.now().isoformat(),
        "processing_language": "Python 3.x"
    }
    
    processing_time = (time.time() - start_time) * 1000  # Convert to milliseconds
    
    # Create result object
    python_result = {
        "id": original_id,
        "python_message": processed_message,
        "processed_by": ["appStarter", "appBridge", "ProcessDataPython"],
        "processing_time": processing_time,
        "analysis": data_analysis
    }
    
    # Emit to next step
    await emit({
        "topic": "python.done",
        "data": python_result
    })
    
    logger.info(f"✅ Python processing completed in {processing_time:.2f}ms")
```

  </Tab>
  <Tab value="notification-handler">
    A TypeScript notification handler that processes the Python results and sends notifications, showing seamless data flow between Python and TypeScript.

```typescript
import { z } from 'zod'

export const config = {
  type: 'event',
  name: 'NotificationHandler',
  description: 'Send notifications after Python processing',
  subscribes: ['python.done'],
  emits: ['notification.sent'],
  input: z.object({
    id: z.number(),
    python_message: z.string(),
    processed_by: z.array(z.string()),
    processing_time: z.number(),
    analysis: z.record(z.unknown()).optional()
  }),
  flows: ['data-processing']
} as const

export const handler = async (input: any, { logger, emit }: any) => {
  logger.info('📧 Sending notifications after Python processing', { id: input.id })
  
  // Simulate sending notifications (email, slack, etc.)
  const notification = {
    id: input.id,
    message: `Notification: ${input.python_message}`,
    processed_by: input.processed_by,
    sent_at: new Date().toISOString()
  }

  // Send notification data to final step
  await emit({
    topic: 'notification.sent',
    data: {
      ...notification,
      processing_time: input.processing_time
    }
  })

  logger.info('✅ Notifications sent successfully', { id: input.id })
}
```

  </Tab>
  <Tab value="finalizer">
    A TypeScript finalizer that aggregates all the processing results and prepares the final summary data before handing off to JavaScript for metrics generation.

```typescript
import { z } from 'zod'

// Final step to complete the app - TypeScript
export const config = {
  type: 'event',
  name: 'AppFinalizer',
  description: 'Complete the basic app and log final results',
  subscribes: ['notification.sent'],
  emits: ['app.completed'],
  input: z.object({
    id: z.number(),
    message: z.string(),
    processed_by: z.array(z.string()),
    sent_at: z.string(),
    processing_time: z.number()
  }),
  flows: ['data-processing']
} as const

export const handler = async (input: any, { logger, emit }: any) => {
  logger.info('🏁 Finalizing app', { 
    notificationId: input.id,
    message: input.message 
  })
  
  // Create final app summary
  const summary = {
    appId: input.id,
    status: 'completed',
    completed_at: new Date().toISOString(),
    steps_executed: [
      'app-starter',
      'app-bridge', 
      'python-processor',
      'notification-handler',
      'app-finalizer'
    ],
    result: input.message
  }

  // Send to JavaScript summary generator
  await emit({
    topic: 'app.completed',
    data: {
      ...summary,
      total_processing_time: input.processing_time
    }
  })

  logger.info('✅ App finalized successfully', { 
    appId: input.id,
    totalSteps: summary.steps_executed.length
  })
}
```

  </Tab>
  <Tab value="summary-generator">
    The final step uses JavaScript for dynamic summary generation and metrics calculation, showcasing how all three languages work together in a single workflow.

```javascript
// Final summary step - JavaScript
export const config = {
  type: 'event',
  name: 'summaryGenerator',
  description: 'Generate final summary in JavaScript',
  subscribes: ['app.completed'],
  emits: [], // Final step - no further processing needed
  flows: ['data-processing']
}

export const handler = async (input, { logger }) => {
  logger.info('📊 Generating final summary in JavaScript', { 
    appId: input.appId,
    status: input.status 
  })
  
  // Calculate processing metrics
  const processingTime = input.total_processing_time || 0
  const stepsCount = input.steps_executed ? input.steps_executed.length : 0
  
  // Create comprehensive summary
  const summary = {
    appId: input.appId,
    finalStatus: input.status,
    totalSteps: stepsCount,
    processingTimeMs: processingTime,
    languages: ['TypeScript', 'Python', 'JavaScript'],
    summary: `Multi-language app completed successfully with ${stepsCount} steps`,
    result: input.result,
    completedAt: new Date().toISOString(),
    generatedBy: 'javascript-summary-step'
  }
  
  // Log final summary (final step - no emit needed)
  logger.info('✨ Final summary generated successfully', summary)
  
  return summary
}
```

  </Tab>
</Tabs>

---

## Type Definitions

Our unified system uses shared TypeScript types to ensure type safety across the multi-language pipeline:

```typescript
// types/index.ts
export interface AppData {
  id: number
  input: Record<string, unknown>
  started_at: string
  traceId: string
}

export interface ProcessedResult {
  original_id: number
  processed_at: string
  result: string
  confidence: number
  model_version: string
}

export interface PythonResult {
  id: number
  python_message: string
  processed_by: string[]
  processing_time: number
}

export interface NotificationData {
  id: number
  message: string
  processed_by: string[]
  sent_at: string
}

export interface AppSummary {
  appId: number
  status: string
  completed_at: string
  steps_executed: string[]
  result: string
}
```

---

## Explore the Workbench

The Motia Workbench provides a visual representation of your multi-language pipeline, making it easy to trace data flow between TypeScript, Python, and JavaScript steps.

<div className="my-8">![Multi-Language Workflow in Motia Workbench](/docs-images/motia-build-your-app-2.gif)</div>

You can monitor real-time execution, view logs from all languages in a unified interface, and trace the complete data flow from the TypeScript API through Python processing to JavaScript summary generation.

---

## Event Flow Architecture

The pipeline follows a clear event-driven flow that connects all languages seamlessly:

1. **`app.started`** - TypeScript API → TypeScript Bridge
2. **`data.processed`** - TypeScript Bridge → Python Processor  
3. **`python.done`** - Python Processor → TypeScript Notification Handler
4. **`notification.sent`** - TypeScript Notification → TypeScript Finalizer
5. **`app.completed`** - TypeScript Finalizer → JavaScript Summary Generator

Each step only needs to know the events it subscribes to and emits, creating loose coupling while maintaining strong data flow guarantees.

---

## Key Features & Benefits

### 🧩 **Step as Universal Primitive**
Every piece of logic—whether TypeScript, Python, or JavaScript—follows the same step pattern, creating true unification.

### 🌐 **Seamless Language Integration**
Steps eliminate the complexity of multi-language systems by providing a unified programming model.

### 📊 **Unified Development Experience**
Write, debug, and monitor all languages through a single interface and shared execution model.

### ⚡ **Hot Reload Across Languages**
Edit any step in any language and see changes instantly across the entire pipeline.

### 🔄 **Event-Driven Communication**
Steps communicate through events, enabling loose coupling and independent scaling.

### 🎯 **Single Deployment Model**
Deploy all languages together as a cohesive system, not as separate microservices.

### 🐍 **Python Step Naming**
Python steps use the `_step.py` suffix convention for proper module resolution (e.g., `simple-python_step.py`).

---

## Trying It Out

Ready to build your first multi-language Motia application? Let's get it running.

<Steps>

### Create Your Motia App

Start by creating a new Motia project with the interactive setup.

```shell
npx motia@latest create
```

### Navigate and Start Development

Move into your project directory and start the development server.

```shell
cd my-app  # Replace with your project name
npm run dev
```

### Open the Workbench

Navigate to [`http://localhost:3000`](http://localhost:3000) to access the Workbench and run your workflow.

### Test the Multi-Language Pipeline

Send a request to your API endpoint to see the multi-language workflow in action:

```shell
   curl -X POST http://localhost:3000/start-app \
     -H "Content-Type: application/json" \
     -d '{"data": {"test": "value"}, "message": "Hello!"}'
```

Watch in the Workbench as your data flows through:
1. **TypeScript** validation and event emission
2. **TypeScript** bridge processing and forwarding  
3. **Python** data processing with rich logging
4. **TypeScript** notification handling
5. **TypeScript** finalization and aggregation
6. **JavaScript** summary generation and metrics

</Steps>

---

## 💻 Dive into the Code

Want to explore multi-language workflows further? Check out additional examples and the complete source code:

<div className="not-prose">
  <div className="bg-gradient-to-r from-blue-50 to-indigo-50 border border-blue-200 rounded-lg p-6 my-6">
    <div className="flex items-start space-x-4">
      <div className="flex-shrink-0">
        <svg className="w-8 h-8 text-blue-600" fill="currentColor" viewBox="0 0 20 20">
          <path fillRule="evenodd" d="M12.316 3.051a1 1 0 01.633 1.265l-4 12a1 1 0 11-1.898-.632l4-12a1 1 0 011.265-.633zM5.707 6.293a1 1 0 010 1.414L3.414 10l2.293 2.293a1 1 0 11-1.414 1.414l-3-3a1 1 0 010-1.414l3-3a1 1 0 011.414 0zm8.586 0a1 1 0 011.414 0l3 3a1 1 0 010 1.414l-3 3a1 1 0 11-1.414-1.414L16.586 10l-2.293-2.293a1 1 0 010-1.414z" clipRule="evenodd" />
        </svg>
      </div>
      <div className="flex-1">
        <h3 className="text-lg font-semibold text-gray-900 mb-2">Multi-Language Examples</h3>
        <p className="text-gray-600 mb-4">Access complete multi-language implementations, configuration examples, and learn how to integrate TypeScript, Python, and JavaScript in production applications.</p>
        <div className="flex flex-col sm:flex-row gap-3">
          <a 
            href="https://github.com/MotiaDev/motia-examples" 
            target="_blank" 
            rel="noopener noreferrer"
            className="inline-flex items-center px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-md transition-colors duration-200"
          >
            <svg className="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 24 24">
              <path d="M12 0C5.374 0 0 5.373 0 12 0 17.302 3.438 21.8 8.207 23.387c.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23A11.509 11.509 0 0112 5.803c1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.30 3.297-1.30.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576C20.566 21.797 24 17.3 24 12c0-6.627-5.373-12-12-12z"/>
            </svg>
            Explore Examples
          </a>
          <a 
            href="/docs/getting-started/quick-start" 
            className="inline-flex items-center px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-800 font-medium rounded-md transition-colors duration-200"
          >
            Quick Start →
          </a>
        </div>
      </div>
    </div>
  </div>
</div>

---

## Conclusion: The Power of Unification Through Steps

This multi-language data processing pipeline demonstrates how **steps** fundamentally change multi-language development. By providing a single primitive that works across TypeScript, Python, and JavaScript, we've eliminated the traditional complexity of polyglot architectures.

**The step primitive enables true unification:**
- **Universal Pattern** - Every step, regardless of language, follows the same receive-process-emit pattern
- **Seamless Integration** - Add Ruby, Go, Rust, or any language using the same step abstraction
- **Unified Deployment** - All languages deploy together as a single, coherent system
- **Shared Development Model** - Write, debug, and monitor everything through the same interface

**Key benefits of step-based unification:**
- **Single Mental Model** - Learn the step pattern once, apply it to any language
- **Cohesive System** - All components work together as parts of one application, not separate services
- **Consistent Experience** - Development, debugging, and monitoring work the same way across all languages
- **Natural Scaling** - Each step can scale independently while maintaining system coherence

**Extend your pipeline with more steps:**
- Add specialized processing steps for different data types and business logic
- Integrate machine learning workflows with Python steps for AI processing
- Build real-time analytics with streaming steps for live data processing
- Connect to enterprise systems through database and API integration steps
- Implement scheduled processing with cron steps for batch operations

The **step primitive** makes all extensions natural and straightforward—every new capability follows the same unified pattern.

Ready to unify your multi-language systems? Start building with steps today!